from examples.example_7_simplest_case_priority_resource_storewrapper.ex_7_model_classes import Trial, g
from vidigi.prep import reshape_for_animations, generate_animation_df
from vidigi.animation import generate_animation, animate_activity_log
import pandas as pd
import plotly.io as pio
pio.renderers.default = "notebook"
import osExample 7: Simplest Case - with a Priority Resource - using new VidigiPriorityStore
In vidigi 0.0.5, a new version of VidigiPriorityStore has been added that can be used almost exactly like a resource - reducing the amount of rewriting required to incorporate vidigi into your model.
This allows us to use the pattern
with self.treatment_cubicles.request(priority=patient.priority) as req:
treatment_resource = yield req
### Continue all code in the indented portion that requires the resource, with the resource
### automatically being returned to the store when the indented portion completesInstead of
treatment_resource = yield self.treatment_cubicles.get(priority=patient.priority)
### Continue all code that requires the resource
self.treatment_cubicles.put(treatment_resource)(even though we are still using a Store behind the scenes)
This minimizes the syntax changes and rewriting that are required when converting an existing model built using resources to a vidigi-compatible state.
When setting up the resources, we simply use the pattern
from vidigi.utils import VidigiPriorityStore, populate_store
...
self.treatment_cubicles = VidigiPriorityStore(self.env)
populate_store(num_resources=g.n_cubicles, # or wherever you are storing your resource counts
simpy_store=self.treatment_cubicles, # pass in the VidgiStore we created
sim_env=self.env # include the simpy env this will sit in
)
import random
import numpy as np
import pandas as pd
import simpy
from sim_tools.distributions import Exponential, Lognormal
from vidigi.resources import VidigiPriorityStore
class g:
'''
Create a scenario to parameterise the simulation model
Parameters:
-----------
random_number_set: int, optional (default=DEFAULT_RNG_SET)
Set to control the initial seeds of each stream of pseudo
random numbers used in the model.
n_cubicles: int
The number of treatment cubicles
trauma_treat_mean: float
Mean of the trauma cubicle treatment distribution (Lognormal)
trauma_treat_var: float
Variance of the trauma cubicle treatment distribution (Lognormal)
arrival_rate: float
Set the mean of the exponential distribution that is used to sample the
inter-arrival time of patients
'''
random_number_set = 42
n_cubicles = 4
trauma_treat_mean = 40
trauma_treat_var = 5
arrival_rate = 5
sim_duration = 600
number_of_runs = 100
class Patient:
'''
Class defining details for a patient entity
'''
def __init__(self, p_id):
'''
Constructor method
Params:
-----
identifier: int
a numeric identifier for the patient.
'''
self.identifier = p_id
self.arrival = -np.inf
self.wait_treat = -np.inf
self.total_time = -np.inf
self.treat_duration = -np.inf
# Randomly initialise a patient priority value
# Lower values will be prioritised - so priority 1 will be seen before priority 2
if random.uniform(0, 1) < 0.2:
self.priority = 1
else:
self.priority = 2
class Model:
'''
Simulates the simplest minor treatment process for a patient
1. Arrive
2. Examined/treated by nurse when one available
3. Discharged
'''
# Constructor to set up the model for a run. We pass in a run number when
# we create a new model.
def __init__(self, run_number):
# Create a SimPy environment in which everything will live
self.env = simpy.Environment()
self.event_log = []
# Create a patient counter (which we'll use as a patient ID)
self.patient_counter = 0
self.patients = []
# Create our resources
self.init_resources()
# Store the passed in run number
self.run_number = run_number
# Create a new Pandas DataFrame that will store some results against
# the patient ID (which we'll use as the index).
self.results_df = pd.DataFrame()
self.results_df["Patient ID"] = [1]
self.results_df["Queue Time Cubicle"] = [0.0]
self.results_df["Time with Nurse"] = [0.0]
self.results_df.set_index("Patient ID", inplace=True)
# Create an attribute to store the mean queuing times across this run of
# the model
self.mean_q_time_cubicle = 0
self.patient_inter_arrival_dist = Exponential(mean = g.arrival_rate,
random_seed = self.run_number*g.random_number_set)
self.treat_dist = Lognormal(mean = g.trauma_treat_mean,
stdev = g.trauma_treat_var,
random_seed = self.run_number*g.random_number_set)
def init_resources(self):
'''
Init the number of resources
and store in the arguments container object
Resource list:
1. Nurses/treatment bays (same thing in this model)
'''
self.treatment_cubicles = VidigiPriorityStore(self.env, num_resources=g.n_cubicles)
# A generator function that represents the DES generator for patient arrivals
def generator_patient_arrivals(self):
# Use an infinite loop here to keep doing this indefinitely while the simulation runs
while True:
# Increment the patient counter by 1 (first patient will have an ID of 1)
self.patient_counter += 1
p = Patient(self.patient_counter)
# Store patient in list for later easy access
self.patients.append(p)
# Tell SimPy to start up the attend_clinic generator function with this patient
# (the generator function that will model the patient's journey through the system)
self.env.process(self.attend_clinic(p))
# Randomly sample the time to the next patient arriving
sampled_inter = self.patient_inter_arrival_dist.sample()
# Freeze this instance of this function in place until the inter-arrival time
# sampled above has elapsed
yield self.env.timeout(sampled_inter)
def attend_clinic(self, patient):
"""
A generator function that represents the pathway for a patient going through the clinic.
The patient object is passed in to the generator function so we can extract information
from / record information to it
"""
self.arrival = self.env.now
# ===== LOGGING FOR VIDIGI ANIMATION ===== #
self.event_log.append(
{'patient': patient.identifier,
'pathway': patient.priority,
'event_type': 'arrival_departure',
'event': 'arrival',
'time': self.env.now}
)
# ========================================= #
# request examination resource
start_wait = self.env.now
# ===== LOGGING FOR VIDIGI ANIMATION ===== #
self.event_log.append(
{'patient': patient.identifier,
'pathway': patient.priority,
'event': 'treatment_wait_begins',
'event_type': 'queue',
'time': self.env.now}
)
# ========================================= #
# Seize a treatment resource when available
# Note that we must pass in the patient priority
with self.treatment_cubicles.request(priority=patient.priority) as req:
treatment_resource = yield req
# record the waiting time for registration
self.wait_treat = self.env.now - start_wait
# ===== LOGGING FOR VIDIGI ANIMATION ===== #
self.event_log.append(
{'patient': patient.identifier,
'pathway': patient.priority,
'event': 'treatment_begins',
'event_type': 'resource_use',
'time': self.env.now,
'resource_id': treatment_resource.id_attribute
}
)
# ========================================= #
# sample treatment duration
self.treat_duration = self.treat_dist.sample()
yield self.env.timeout(self.treat_duration)
# ===== LOGGING FOR VIDIGI ANIMATION ===== #
self.event_log.append(
{'patient': patient.identifier,
'pathway': patient.priority,
'event': 'treatment_complete',
'event_type': 'resource_use_end',
'time': self.env.now,
'resource_id': treatment_resource.id_attribute}
)
# ========================================= #
# total time in system
self.total_time = self.env.now - self.arrival
# ===== LOGGING FOR VIDIGI ANIMATION ===== #
self.event_log.append(
{'patient': patient.identifier,
'pathway': patient.priority,
'event': 'depart',
'event_type': 'arrival_departure',
'time': self.env.now}
)
# ========================================= #
# This method calculates results over a single run. Here we just calculate
# a mean, but in real world models you'd probably want to calculate more.
def calculate_run_results(self):
# Take the mean of the queuing times across patients in this run of the
# model.
self.mean_q_time_cubicle = self.results_df["Queue Time Cubicle"].mean()
# The run method starts up the DES entity generators, runs the simulation,
# and in turns calls anything we need to generate results for the run
def run(self):
# Start up our DES entity generators that create new patients. We've
# only got one in this model, but we'd need to do this for each one if
# we had multiple generators.
self.env.process(self.generator_patient_arrivals())
# Run the model for the duration specified in g class
self.env.run(until=g.sim_duration)
# Now the simulation run has finished, call the method that calculates
# run results
self.calculate_run_results()
self.event_log = pd.DataFrame(self.event_log)
self.event_log["run"] = self.run_number
return {'results': self.results_df, 'event_log': self.event_log}
# Class representing a Trial for our simulation - a batch of simulation runs.
class Trial:
# The constructor sets up a pandas dataframe that will store the key
# results from each run against run number, with run number as the index.
def __init__(self):
self.df_trial_results = pd.DataFrame()
self.df_trial_results["Run Number"] = [0]
self.df_trial_results["Arrivals"] = [0]
self.df_trial_results["Mean Queue Time Cubicle"] = [0.0]
self.df_trial_results.set_index("Run Number", inplace=True)
self.all_event_logs = []
# Method to run a trial
def run_trial(self):
print(f"{g.n_cubicles} nurses")
print("") ## Print a blank line
# Run the simulation for the number of runs specified in g class.
# For each run, we create a new instance of the Model class and call its
# run method, which sets everything else in motion. Once the run has
# completed, we grab out the stored run results (just mean queuing time
# here) and store it against the run number in the trial results
# dataframe.
for run in range(g.number_of_runs):
random.seed(run)
my_model = Model(run)
model_outputs = my_model.run()
patient_level_results = model_outputs["results"]
event_log = model_outputs["event_log"]
self.df_trial_results.loc[run] = [
len(patient_level_results),
my_model.mean_q_time_cubicle,
]
# print(event_log)
self.all_event_logs.append(event_log)
self.all_event_logs = pd.concat(self.all_event_logs)my_trial = Trial()
my_trial.run_trial()4 nurses
my_trial.all_event_logs.head(50)| patient | pathway | event_type | event | time | resource_id | run | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | arrival_departure | arrival | 0.000000 | NaN | 0 |
| 1 | 1 | 2 | queue | treatment_wait_begins | 0.000000 | NaN | 0 |
| 2 | 1 | 2 | resource_use | treatment_begins | 0.000000 | 1.0 | 0 |
| 3 | 2 | 2 | arrival_departure | arrival | 3.399660 | NaN | 0 |
| 4 | 2 | 2 | queue | treatment_wait_begins | 3.399660 | NaN | 0 |
| 5 | 2 | 2 | resource_use | treatment_begins | 3.399660 | 2.0 | 0 |
| 6 | 3 | 2 | arrival_departure | arrival | 8.497645 | NaN | 0 |
| 7 | 3 | 2 | queue | treatment_wait_begins | 8.497645 | NaN | 0 |
| 8 | 3 | 2 | resource_use | treatment_begins | 8.497645 | 3.0 | 0 |
| 9 | 4 | 2 | arrival_departure | arrival | 8.596678 | NaN | 0 |
| 10 | 4 | 2 | queue | treatment_wait_begins | 8.596678 | NaN | 0 |
| 11 | 4 | 2 | resource_use | treatment_begins | 8.596678 | 4.0 | 0 |
| 12 | 5 | 2 | arrival_departure | arrival | 8.608025 | NaN | 0 |
| 13 | 5 | 2 | queue | treatment_wait_begins | 8.608025 | NaN | 0 |
| 14 | 6 | 2 | arrival_departure | arrival | 11.359739 | NaN | 0 |
| 15 | 6 | 2 | queue | treatment_wait_begins | 11.359739 | NaN | 0 |
| 16 | 7 | 2 | arrival_departure | arrival | 19.509442 | NaN | 0 |
| 17 | 7 | 2 | queue | treatment_wait_begins | 19.509442 | NaN | 0 |
| 18 | 8 | 2 | arrival_departure | arrival | 22.877356 | NaN | 0 |
| 19 | 8 | 2 | queue | treatment_wait_begins | 22.877356 | NaN | 0 |
| 20 | 9 | 2 | arrival_departure | arrival | 26.653863 | NaN | 0 |
| 21 | 9 | 2 | queue | treatment_wait_begins | 26.653863 | NaN | 0 |
| 22 | 1 | 2 | resource_use_end | treatment_complete | 40.317385 | 1.0 | 0 |
| 23 | 1 | 2 | arrival_departure | depart | 40.317385 | NaN | 0 |
| 24 | 5 | 2 | resource_use | treatment_begins | 40.317385 | 1.0 | 0 |
| 25 | 10 | 2 | arrival_departure | arrival | 40.737793 | NaN | 0 |
| 26 | 10 | 2 | queue | treatment_wait_begins | 40.737793 | NaN | 0 |
| 27 | 2 | 2 | resource_use_end | treatment_complete | 42.443230 | 2.0 | 0 |
| 28 | 2 | 2 | arrival_departure | depart | 42.443230 | NaN | 0 |
| 29 | 6 | 2 | resource_use | treatment_begins | 42.443230 | 2.0 | 0 |
| 30 | 4 | 2 | resource_use_end | treatment_complete | 48.809628 | 4.0 | 0 |
| 31 | 4 | 2 | arrival_departure | depart | 48.809628 | NaN | 0 |
| 32 | 7 | 2 | resource_use | treatment_begins | 48.809628 | 4.0 | 0 |
| 33 | 3 | 2 | resource_use_end | treatment_complete | 51.483457 | 3.0 | 0 |
| 34 | 3 | 2 | arrival_departure | depart | 51.483457 | NaN | 0 |
| 35 | 8 | 2 | resource_use | treatment_begins | 51.483457 | 3.0 | 0 |
| 36 | 11 | 2 | arrival_departure | arrival | 71.026558 | NaN | 0 |
| 37 | 11 | 2 | queue | treatment_wait_begins | 71.026558 | NaN | 0 |
| 38 | 5 | 2 | resource_use_end | treatment_complete | 77.447488 | 1.0 | 0 |
| 39 | 5 | 2 | arrival_departure | depart | 77.447488 | NaN | 0 |
| 40 | 9 | 2 | resource_use | treatment_begins | 77.447488 | 1.0 | 0 |
| 41 | 6 | 2 | resource_use_end | treatment_complete | 83.962251 | 2.0 | 0 |
| 42 | 6 | 2 | arrival_departure | depart | 83.962251 | NaN | 0 |
| 43 | 10 | 2 | resource_use | treatment_begins | 83.962251 | 2.0 | 0 |
| 44 | 12 | 2 | arrival_departure | arrival | 87.458700 | NaN | 0 |
| 45 | 12 | 2 | queue | treatment_wait_begins | 87.458700 | NaN | 0 |
| 46 | 13 | 2 | arrival_departure | arrival | 87.465138 | NaN | 0 |
| 47 | 13 | 2 | queue | treatment_wait_begins | 87.465138 | NaN | 0 |
| 48 | 7 | 2 | resource_use_end | treatment_complete | 95.498040 | 4.0 | 0 |
| 49 | 7 | 2 | arrival_departure | depart | 95.498040 | NaN | 0 |
STEP_SNAPSHOT_MAX = 45
LIMIT_DURATION = g.sim_duration
WRAP_QUEUES_AT = 15full_patient_df = reshape_for_animations(
event_log=my_trial.all_event_logs[my_trial.all_event_logs['run']==1],
every_x_time_units=2,
entity_col_name="patient",
step_snapshot_max=STEP_SNAPSHOT_MAX,
limit_duration=LIMIT_DURATION,
debug_mode=True
)
full_patient_df.head(15)Iteration through time-unit-by-time-unit logs complete 12:37:59
Snapshot df concatenation complete at 12:37:59
| index | patient | pathway | event_type | event | time | resource_id | run | rank | snapshot_time | additional | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 0 | NaN |
| 1 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 2 | NaN |
| 2 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 4 | NaN |
| 3 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 6 | NaN |
| 4 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 8 | NaN |
| 5 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 10 | NaN |
| 6 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 12 | NaN |
| 7 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 14 | NaN |
| 8 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 16 | NaN |
| 9 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 18 | NaN |
| 10 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 20 | NaN |
| 11 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 22 | NaN |
| 12 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 24 | NaN |
| 13 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 26 | NaN |
| 14 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 28 | NaN |
event_position_df = pd.DataFrame([
{'event': 'arrival',
'x': 50, 'y': 300,
'label': "Arrival" },
# Triage - minor and trauma
{'event': 'treatment_wait_begins',
'x': 205, 'y': 275,
'label': "Waiting for Treatment"},
{'event': 'treatment_begins',
'x': 205, 'y': 175,
'resource':'n_cubicles',
'label': "Being Treated"},
{'event': 'exit',
'x': 270, 'y': 70,
'label': "Exit"}
])Generate animation using the step-by-step functions
Using the three step-by-step functions allows us to intervene in the produced dataframe and manually take control of the icons in use.
This will allow us to show the high-priority patients with a unique icon so we can see their frequency and how they are handled in the final model.
full_patient_df_plus_pos = generate_animation_df(
full_entity_df=full_patient_df,
event_position_df=event_position_df,
entity_col_name="patient",
wrap_queues_at=WRAP_QUEUES_AT,
step_snapshot_max=STEP_SNAPSHOT_MAX,
gap_between_entities=10,
gap_between_resources=10,
gap_between_resource_rows=30,
gap_between_queue_rows=30,
debug_mode=True
)
full_patient_df_plus_pos.sort_values(['patient', 'snapshot_time']).head(15)Placement dataframe finished construction at 12:37:59
| index | patient | pathway | event_type | event | time | resource_id | run | rank | snapshot_time | additional | x | y_final | label | resource | x_final | row | icon | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10186 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 0 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10187 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 2 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10188 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 4 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10189 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 6 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10190 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 8 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10191 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 10 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10192 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 12 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10193 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 14 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10194 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 16 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10195 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 18 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10196 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 20 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10197 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 22 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10198 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 24 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10199 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 26 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
| 10200 | 2 | 1 | 1 | resource_use | treatment_begins | 0.0 | 1.0 | 1 | 1.0 | 28 | NaN | 205 | 175.0 | Being Treated | n_cubicles | 205.0 | 0.0 | 🧔🏼 |
def show_priority_icon(row):
if "more" not in row["icon"]:
if row["pathway"] == 1:
return "🚨"
else:
return row["icon"]
else:
return row["icon"]full_patient_df_plus_pos = full_patient_df_plus_pos.assign(
icon=full_patient_df_plus_pos.apply(show_priority_icon, axis=1)
)full_patient_df_plus_pos.head(15)| index | patient | pathway | event_type | event | time | resource_id | run | rank | snapshot_time | additional | x | y_final | label | resource | x_final | row | icon | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 155 | 49 | 2 | queue | exit | 208.540636 | NaN | 1 | 1.0 | 598 | NaN | 270 | 70.0 | Exit | NaN | 270.0 | 0.0 | 🧕🏾 |
| 1 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 208 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 2 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 210 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 3 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 212 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 4 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 214 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 5 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 216 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 6 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 218 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 7 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 220 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 8 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 222 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 9 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 224 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 10 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 226 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 11 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 228 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 12 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 230 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 13 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 27.0 | 232 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 95.0 | 1.0 | 🧕🏾 |
| 14 | 155 | 49 | 2 | queue | treatment_wait_begins | 206.540636 | NaN | 1 | 26.0 | 234 | NaN | 205 | 305.0 | Waiting for Treatment | NaN | 105.0 | 1.0 | 🧕🏾 |
generate_animation(
full_entity_df_plus_pos=full_patient_df_plus_pos.sort_values(['patient', 'snapshot_time']),
event_position_df= event_position_df,
scenario=g(),
entity_col_name="patient",
debug_mode=True,
setup_mode=False,
include_play_button=True,
entity_icon_size=20,
resource_icon_size=20,
gap_between_resource_rows=30,
plotly_height=700,
frame_duration=800,
frame_transition_duration=200,
plotly_width=1200,
override_x_max=300,
override_y_max=500,
time_display_units="dhm",
display_stage_labels=False,
add_background_image="https://raw.githubusercontent.com/Bergam0t/vidigi/refs/heads/main/examples/example_1_simplest_case/Simplest%20Model%20Background%20Image%20-%20Horizontal%20Layout.drawio.png",
)Output animation generation complete at 12:38:11
Rerun, but using the all-in-one animation function (which will not show different priority icons)
animate_activity_log(
event_log=my_trial.all_event_logs[my_trial.all_event_logs['run']==1],
event_position_df= event_position_df,
scenario=g(),
entity_col_name="patient",
debug_mode=True,
setup_mode=False,
every_x_time_units=1,
include_play_button=True,
entity_icon_size=20,
resource_icon_size=20,
gap_between_entities=6,
gap_between_queue_rows=25,
gap_between_resource_rows=25,
plotly_height=700,
frame_duration=200,
plotly_width=1200,
override_x_max=300,
override_y_max=500,
limit_duration=g.sim_duration,
wrap_queues_at=25,
step_snapshot_max=125,
time_display_units="dhm",
display_stage_labels=False,
add_background_image="https://raw.githubusercontent.com/Bergam0t/vidigi/refs/heads/main/examples/example_1_simplest_case/Simplest%20Model%20Background%20Image%20-%20Horizontal%20Layout.drawio.png",
)Animation function called at 12:39:05
Iteration through time-unit-by-time-unit logs complete 12:39:08
Snapshot df concatenation complete at 12:39:08
Reshaped animation dataframe finished construction at 12:39:08
Placement dataframe finished construction at 12:39:08
Output animation generation complete at 12:39:11
Total Time Elapsed: 5.72 seconds